package cn.edu.dgut.sai.jpa;

import com.mysql.cj.jdbc.MysqlDataSource;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.*;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.http.MediaType;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

import javax.persistence.*;
import javax.sql.DataSource;
import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toList;

@SuppressWarnings("unused")
interface UserRepositories extends JpaRepository<User, Long> {

    Optional<User> getUserByUsername(String username);

}

interface JpaService {
    @Transactional
    void testEntityStatus();

    void testJpa();

    Stream<User> getAllUser();
}

/**
 * @author Sai
 * Created by Sai on 2019/9/12.
 */
@EnableWebMvc
@Configuration
@ComponentScan
@EnableJpaRepositories("cn.edu.dgut.sai.jpa")
@EnableTransactionManagement
@PropertySource("classpath:sai.properties")// 增加属性源
public class JpaAppDemo {

    private final Environment env;

    public JpaAppDemo(Environment env) {
        this.env = env;
    }

    // 配置H2数据源
    @Bean
    public DataSource dataSource() {
        EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
        return builder
                .setType(EmbeddedDatabaseType.H2)
//                .addScript("classpath:data.sql")
                .build();
    }

    // 配置MySql数据源
    @Bean
    @Primary// 设置主数据库
    public DataSource jdbcDataSource() {

        MysqlDataSource source = new MysqlDataSource();
        // 从属性文件读取属性值
//        source.setUrl(env.getProperty("sai.datasource.url"));
//        source.setUser(env.getProperty("sai.datasource.username"));
//        source.setPassword(env.getProperty("sai.datasource.password"));// 读取属性文件中的password属性

        // 从系统环境变量读取属性值
        // 在Idea的tomcat配置文件中，设置启动环境属性变量
        source.setUrl(System.getenv("sai.datasource.url"));
        source.setUser(System.getenv("sai.datasource.username"));
        source.setPassword(System.getenv("sai.datasource.password"));
        return source;
    }

    // 配置entityManagerFactory
    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {

        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setGenerateDdl(true);
        // 显示sql语句
        vendorAdapter.setShowSql(true);
        vendorAdapter.setDatabasePlatform("org.hibernate.dialect.MySQL8Dialect");// Mysql 8.X

        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setJpaVendorAdapter(vendorAdapter);

        factory.setPackagesToScan("cn.edu.dgut.sai.jpa");

        factory.setDataSource(dataSource);
        return factory;
    }

    // 配置事务
    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {

        JpaTransactionManager txManager = new JpaTransactionManager();
        txManager.setEntityManagerFactory(entityManagerFactory);
        return txManager;
    }
}

@Entity
@Data
class User {

    @Id
    @GeneratedValue
    private long id;

    @Column(name = "name", unique = true, nullable = false, length = 200)
    private String username;

    private String firstname;
    private String lastname;
}

@Service
class CustomJpaService implements JpaService, InitializingBean, DisposableBean {

    private final UserRepositories userRepo;
    private final OrderRepository orderRepository;
    private final ItemRepository itemRepository;

    public CustomJpaService(UserRepositories userRepo, OrderRepository orderRepository, ItemRepository itemRepository) {
        this.userRepo = userRepo;
        this.orderRepository = orderRepository;
        this.itemRepository = itemRepository;
    }

    @Override
    @Transactional
    public void testEntityStatus() {
        Order order = new Order();
        order.setName("order1");
        Item item1 = new Item();

        item1.setName("item1_order1");
        item1.setOrder(order);

        Item item2 = new Item();
        item2.setName("item2_order1");
        item2.setOrder(order);

        List<Item> items = new ArrayList<>();
        items.add(item1);
        items.add(item2);
        order.setItems(items);

        System.out.println(order.getId());

        orderRepository.save(order);

        System.out.println(order.getId());

        order.setName("order1_updated");

        items.get(0).setName("item1_order1_updated");
        items.get(1).setName("item2_order1_updated");

//        itemRepository.flush();
//        itemRepository.findAll();
        orderRepository.delete(order);
//        itemRepository.deleteAll(items);
//        orderRepository.flush(); // 切换 注释这条语句

    }

    @Transactional
    public void testJpa() {
        userRepo.findAll().forEach(System.out::println);

        userRepo.getUserByUsername("Sai").ifPresent(
                System.out::println
        );

        userRepo.getUserByUsername("Sai").map(User::getFirstname);

        try {
            userRepo.getUserByUsername("Zhang").orElseThrow(RuntimeException::new);
        } catch (Exception e) {
            System.out.println("NPE异常抛出: " + e.getMessage());
        }
    }

    @Override
    public Stream<User> getAllUser() {
        return userRepo.findAll().stream();
    }

    // Bean初始化时向数据库增加原始数据
    @Override
    public void afterPropertiesSet() {
        User user = new User();
        user.setUsername("Sai");
        user.setFirstname("Li");
        user.setLastname("Zhixing");

        userRepo.save(user);
    }

    // 程序关闭时，删除所有数据
    @Override
    public void destroy() {
        userRepo.deleteAll();
    }
}

@Controller
class testController {
    private final JpaService jpaService;

    testController(JpaService jpaService) {
        this.jpaService = jpaService;
    }


    @RequestMapping(value = "/testJson", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    @ResponseBody
    public UserVO testJson() {

        UserVO userVO = new UserVO();

        BeanUtils.copyProperties(jpaService.getAllUser().collect(toList()).get(0), userVO);

        return userVO;
    }

}

@Getter
@Setter
@NoArgsConstructor
class UserVO {
    private String firstname;
    private String lastname;
}
